core_code_seg_sel equ 0x38          ;内核代码段选择子
core_data_seg_sel equ 0x30          ;内核数据段选择子
sys_routine_seg_sel equ 0x28        ;系统公共例程代码段的选择子
video_ram_seg_sel equ 0x20          ;视频显示缓冲区的段选择子
core_stack_seg_sel equ 0x18         ;内核堆栈段选择子
mem_0_4_gb_seg_sel equ 0x08         ;整个0-4GB内存的段的选择子

;以下是系统核心的头部，用于加载核心程序
core_length     dd core_end                     ;核心程序总长度      #00
sys_routine_seg dd section.sys_routine.start    ;系统公用例程段位置  #04
core_data_seg   dd section.core_data.start      ;核心数据段位置      #08
core_code_seg   dd section.core_code.start      ;核心代码段位置      #0c
core_entry      dd start                        ;核心代码段入口点    #10
                dw core_code_seg_sel            

;核心数据段
         pgdt             dw  0             ;用于设置和修改GDT 
                          dd  0

         ram_alloc        dd  0x00100000    ;下次分配内存时的起始地址

         ;符号地址检索表
         salt:
         salt_1           db  '@PrintString'
                     times 256-($-salt_1) db 0
                          dd  put_string
                          dw  sys_routine_seg_sel

         salt_2           db  '@ReadDiskData'
                     times 256-($-salt_2) db 0
                          dd  read_hard_disk_0
                          dw  sys_routine_seg_sel

         salt_3           db  '@PrintDwordAsHexString'
                     times 256-($-salt_3) db 0
                          dd  put_hex_dword
                          dw  sys_routine_seg_sel

         salt_4           db  '@TerminateProgram'
                     times 256-($-salt_4) db 0
                          dd  return_point
                          dw  core_code_seg_sel

         salt_item_len   equ $-salt_4
         salt_items      equ ($-salt)/salt_item_len

         message_1        db  '  If you seen this message,that means we '
                          db  'are now in protect mode,and the system '
                          db  'core is loaded,and the video display '
                          db  'routine works perfectly.',0x0d,0x0a,0

         message_2        db  '  System wide CALL-GATE mounted.',0x0d,0x0a,0

         message_3        db  0x0d,0x0a,'  Loading user program...',0
         
         do_status        db  'Done.',0x0d,0x0a,0
         
         message_6        db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
                          db  '  User program terminated,control returned.',0

         bin_hex          db '0123456789ABCDEF'
                                            ;put_hex_dword子过程用的查找表 

         core_buf   times 2048 db 0         ;内核用的缓冲区

         esp_pointer      dd 0              ;内核用来临时保存自己的栈指针     

         cpu_brnd0        db 0x0d,0x0a,'  ',0
         cpu_brand  times 52 db 0
         cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0

         ;任务控制块链
         tcb_chain        dd  0

core_data_end:

[bits 32]
SECTION sys_routine vstart = 0                  ;系统公共例程代码段

read_hard_disk_with_gate:                       ;从硬盘读取一个逻辑扇区
                                                ;输入：PUSH 逻辑扇区号
                                                ;     PUSH  目标缓冲区所在段的选择子
                                                ;     PUSH  目标缓冲区在段内的偏移量
                                                ;返回：无
    push eax
    push ebx
    push ecx

    mov ax,[esp + 0x10]                         ;获取调用者的CS
    arpl [esp+0x18],ax                          ;将数据段选择子调整到真实的请求特权级别
    mov ds,[esp + 0x18]                         ;用真实的段选择子加载段寄存器DS
    mov eax,[esp+0x1c]                          ;从栈中取得逻辑扇区号
    mov ebx,[esp + 0x14]                        ;从栈中取得缓冲区在段内的偏移量

set_up_gdt_descriptor:                          ;在GDT内安装一个新的描述符
                                                ;输入：EDX:EAX = 描述符
                                                ;输出：CX = 描述符的选择子
    PUSH eax
    push ebx
    push edx

    push ds
    push es

    mov ebx,core_data_seg_sel                   ;切换到核心数据段
    mov ds,ebx

    sgdt [pgdt]                                 ;以便开始处理GDT

    mov ebx,mem_0_4_gb_seg_sel
    mov es,ebx

    movzx ebx,word [pgdt]                       ;GDT界限
    inc bx                                      ;GDT总字节数，也是下一个描述符偏移
    add ebx,[pgdt + 2]                          ;下一个描述符的线性地址

    mov [es:ebx],eax
    mov [es:ebx+4],edx                          

    add word [pgdt],8

    ldgt [pgdt]

    mov ax,[pgdt]
    xor dx,dx
    mov bx,8
    div bx
    mov cx,ax
    shl cx,3

    pop es
    pop ds
    pop eax

    retf

make_seg_descriptor:                            ;构造存储器和系统的段描述符
                                                ;输入：EAX = 线性基地址
                                                ;      EBX = 段界限
                                                ;ECX = 属性，各属性都在原始的位置，无关的位清零
                                                ;返回：EDX:EAX = 描述符
    mov edx,eax
    shl eax,16
    or ax,bx                                    ;描述符前32位（EAX）构造完毕
    and edx,0xffff0000                          ;清除基地址中无关的位
    rol edx,8                                   
    bswap edx                                   ;装配基址的31~24和23~16  (80486+)

    xor bx,bx
    or edx,ebx                                  ;装配段界限的高4位

    or edx,ecx                                  ;装配属性

    retf

make_gate_descriptor:                           ;构造门的描述符（调用门等）
                                                ;输入：EAX = 门代码在段内偏移地址
                                                ;      BX = 门代码所在段的选择子
                                                ;      CX = 段类型及属性等（各属性位都在原始位置）
                                                ;返回： EDX:EAX = 完整的描述符
    PUSH ebx
    push ecx

    mov edx,eax
    mov dx,cx
    
    and eax,0x0000ffff
    shl ebx,16
    or eax,ebx

    pop ecx
    pop ebx

    retf

fill_descriptor_in_ldt:                         ;在LDT内安装一个新的描述符
                                                ;输入：EDX:EAX = 描述符
                                                ;      EBX = TCB基地址
                                                ;输出:CX = 描述符的选择子
    push eax
    push edx
    push edi
    push ds 

    mov ecx,mem_0_4_gb_seg_sel
    mov ds,ecx

    mov edi,[ebx + 0x0c]                        ;获得LDT基地址

    xor ecx,ecx
    mov cx,[ebx + 0x0a]
    inc cx

    mov [edi + ecx + 0x00],eax
    mov [edi + ecx + 0x04],edx

    add cx,8
    dec,cx

    mov [ebx + 0x0a],cx

    mov ax,cx
    xor,dx,dx
    mov cx,8
    div cx

    mov cx,ax
    shl cx,3
    or cx,0000_0000_0000_0100B

    pop ds
    pop edi
    pop edx
    pop eax
    
    ret

load_relocate_program:                      ;加载并重定位用户程序
                                            ;输入: PUSH 逻辑扇区号
                                            ;      PUSH 任务控制块基地址
                                            ;输出：无
    pushad
    push ds
    push es

    mov ebp,esp

    mov ecx,mem_0_4_gb_seg_sel
    mov es,ecx

    mov esi,[ebp + 11*4]

    mov ecx,160
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi + 0x0c],ecx
    mov word [es:esi+0x0a],0xffff

    mov eax,core_data_seg_sel
    mov ds,eax

    mov eax,[ebp + 12*4]
    mov ebx,core_buf
    call sys_routine_seg_sel:read_hard_disk_0
    ;以下判断整个程序有多大
    mov eax,[core_buf]                 ;程序尺寸
    mov ebx,eax
    and ebx,0xfffffe00                 ;使之512字节对齐（能被512整除的数低 
    add ebx,512                        ;9位都为0 
    test eax,0x000001ff                ;程序的大小正好是512的倍数吗? 
    cmovnz eax,ebx                     ;不是。使用凑整的结果
      
    mov ecx,eax                        ;实际需要申请的内存数量
    call sys_routine_seg_sel:allocate_memory
    mov [es:esi+0x06],ecx              ;登记程序加载基地址到TCB中
    
    mov ebx,ecx
    xor edx,edx
    mov ecx,512
    div ecx
    mov ecx,eax

    mov eax,mem_0_4_gb_seg_sel
    mov ds,eax

    mov eax,[ebp + 12*4]

.b1:
    call sys_routine_seg_sel:read_hard_disk_0
    inc eax
    loop .b1

    mov edi,[es:esi + 0x06]

    ;建立程序头部段描述符
    mov eax,edi                         ;程序头部起始线性地址
    mov ebx.[edi + 0x04]                ;段长度
    dec ebx                             ;段界限
    mov ecx,0x0040f200                  ;字节粒度的数据段描述符，特权级3
    call sys_routine_seg_sel:make_gate_descriptor

    mov ebx,esi
    call fill_descriptor_in_ldt

    or cx,0000_0000_0000_0011B
    mov [es:esi + 0x44],cx
    mov [edi+0x04],cx

    mov eax,edi
    add eax,[edi + 0x0c]
    mov ebx,[edi + 0x10]
    dec ebx
    mov ecx,0x0040f800
    call sys_routine_seg_selp:make_seg_descriptor
    mov ebx,esi
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B
    mov [edi+0x0c],cx

             ;建立程序数据段描述符
    mov eax,edi
    add eax,[edi+0x14]                 ;数据段起始线性地址
    mov ebx,[edi+0x18]                 ;段长度
    dec ebx                            ;段界限 
    mov ecx,0x0040f200                 ;字节粒度的数据段描述符，特权级3
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi                        ;TCB的基地址
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    mov [edi+0x14],cx                  ;登记数据段选择子到头部

         ;建立程序堆栈段描述符
    mov eax,edi
    add eax,[edi+0x1c]                 ;数据段起始线性地址
    mov ebx,[edi+0x20]                 ;段长度
    dec ebx                            ;段界限
    mov ecx,0x0040f200                 ;字节粒度的堆栈段描述符，特权级3
    call sys_routine_seg_sel:make_seg_descriptor
    mov ebx,esi                        ;TCB的基地址
    call fill_descriptor_in_ldt
    or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    mov [edi+0x1c],cx                  ;登记堆栈段选择子到头部
